package org.softee.numbertext;

import static java.math.BigInteger.ZERO;

import java.math.BigDecimal;
import java.math.BigInteger;

public class NumberTextEn extends NumberText {
    private static final String MINUS = "minus";
    private static final String[] ONES = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
        "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
    private static final String[] TENS = {"(zero)", "(ten)", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
    private static final String HUNDRED = "hundred";
    // See http://en.wikipedia.org/wiki/Names_of_large_numbers
    private static final String[] ORDER3 = {"", "thousand", "million", "billion", "trillion", "quadrillion",
        "quintillion", "sextillion", "septillion", "octillion", "nonillion", "decillion",
        "undecillion", "duodecillion", "tredecillion", "quattuordecillion", "quindecillion", "sexdecillion",
        "septendecillion", "octodecillion", "novemdecillion"};

    private final int RESERVE = 200;

    public NumberTextEn() {
        super(MINUS);
    }

    @Override
    public BigDecimal parse(String numberText) {
        throw new UnsupportedOperationException("parse");
    }

    @Override
    protected String formatPositive(final BigInteger number) {
        if (number.equals(ZERO)) {
            return ONES[0];
        }
        StringBuilder sb = new StringBuilder(RESERVE);
        BigInteger remainder = number;
        int level = 0; // start with ones, then thousands, millions ...
        while (!remainder.equals(ZERO)) {
            if (level == ORDER3.length) {
                throw new IllegalArgumentException("Number too large: " + number);
            }
            String unit = ORDER3[level++];
            BigInteger[] next = remainder.divideAndRemainder(BIG_THOUSAND);
            remainder = next[0];
            int hundreds = next[1].intValue();
            // no point in rendering "zero something"
            if (hundreds != 0) {
                if (sb.length() > 0) {
                    sb.insert(0, SPACE);
                }
                String levelText = hundreds(hundreds, unit);
                sb.insert(0, levelText);
            }
        }
        return sb.toString();
    }

    protected String hundreds(int number, String unit) {
        checkRange(number, 0, 999);
        int hundreds = number / 100;
        int remainder = number % 100;
        StringBuilder sb = new StringBuilder(RESERVE);
        if (hundreds != 0) {
            sb.append(ONES[hundreds]).append(SPACE).append(HUNDRED);
        }
        if (hundreds != 0 && remainder != 0) {
            sb.append(SPACE);
        }
        if (remainder != 0 || hundreds == 0) {
            tens(sb, remainder);
        }
        if (!unit.isEmpty()) {
            sb.append(SPACE).append(unit);
        }
        return sb.toString();
    }

    protected void tens(StringBuilder sb, int i) {
        checkRange(i, 0, 99);
        if (i < 20) {
            sb.append(ONES[i]);
        } else {
            int tens = i / 10;
            int ones = i % 10;
            if (tens != 0) {
                sb.append(TENS[tens]);
                if (ones != 0) {
                    //TODO should tens and ones be separated by space in all English-language numbers?
                    sb.append(SPACE);
                }
            }
            if (ones != 0) {
                sb.append(ONES[ones]);
            }
        }
    }
}
